package com.beerandjesus.blackjack.controller;

import com.beerandjesus.blackjack.deck.DeckOfCards;
import com.beerandjesus.blackjack.event.GameEvents;
import com.beerandjesus.blackjack.exception.BetExceedsBankException;
import com.beerandjesus.blackjack.ui.CardDisplayer;
import com.beerandjesus.blackjack.ui.GameEventDisplayer;
import com.beerandjesus.blackjack.ui.GameStatus;

import java.util.Observable;

/**
 * User: reverend
 * Date: Jul 3, 2005
 * Time: 8:09:09 PM
 */
public class GameController extends Observable {

    private PlayerHandController player;
    private DealerHandController dealer;
    private DeckOfCards deck;
    private double bet = 0;
    private double playerBank = 0;
    //    private Observer handObserver;
    private CardDisplayer cardDisplayer;
    private GameEventDisplayer eventDisplayer;
    private GameStatus gameStatus;

    public GameController(CardDisplayer cardDisplayer,
                          GameEventDisplayer eventDisplayer) {
        this.cardDisplayer = cardDisplayer;
        this.gameStatus = new GameStatus();
        this.eventDisplayer = eventDisplayer;
        deck = new DeckOfCards();
        player = new PlayerHandController();
        player.setGameController(this);
        dealer = new DealerHandController();
        dealer.setGameController(this);
    }

    public void dealNewGame() {
        refreshForNewDeal();
        setChanged();
        notifyObservers(GameEvents.DISABLE_BUTTONS);
        dealToPlayer();
        dealToDealer();
        dealToPlayer();
        dealToDealer();
    }

    public void refreshForNewDeal() {
        gameStatus.clear();
        player.clear();
        dealer.clear();
        getDeck().refresh();
    }

    public HandController getPlayerHand() {
        return player;
    }

    public HandController getDealerHand() {
        return dealer;
    }

    public void placeBet(double amount) throws BetExceedsBankException {
        if (amount > playerBank) {
            throw new BetExceedsBankException();
        }
        playerBank = playerBank - amount;
        bet = amount;
    }

    public void dealToPlayer() {
        dealTo(player);
    }

    public void dealToDealer() {
        dealTo(dealer);
    }

    public double getBet() {
        return bet;
    }

    public double getPlayerBank() {
        return playerBank;
    }

    public void setPlayerBank(double amount) {
        this.playerBank = amount;
    }

    public void playOutDealer() {
        if (!dealer.mustPlay()) {
            dealer.stay();
        }
        while (dealer.mustPlay()) {
            dealToDealer();
        }
        dealer.stay();
    }

    public void checkForBlackjack() {
        if (player.isBlackjack() && dealer.isBlackjack()) {
            push();
        } else if (dealer.isBlackjack()) {
            eventDisplayer.displayDealerBlackjack();
            dealerWin();
        } else if (player.isBlackjack()) {
            eventDisplayer.displayPlayerBlackjack();
            playerWin();
        } else {
            setChanged();
            notifyObservers(GameEvents.ENABLE_BUTTONS);
        }
    }

    private void push() {
        playerBank += bet;
        clearCurrentBet();
        eventDisplayer.displayPush();
        gameStatus.push();
    }

    private void clearCurrentBet() {
        bet = 0;
    }

    private void dealerWin() {
        eventDisplayer.displayDealerWin();
        gameStatus.dealerWin();
        clearCurrentBet();
    }

    private void dealerBust() {
        eventDisplayer.displayDealerBust();
        playerWin();
    }

    private void playerWin() {
        double win = bet * 2;
        if (player.isBlackjack()) {
            win = bet * 2.5;
        }
        playerBank = playerBank + win;
        clearCurrentBet();
        eventDisplayer.displayPlayerWin();
        gameStatus.playerWin();
    }

    public void playerBust() {
        eventDisplayer.displayPlayerBust();
        dealerWin();
    }

    private void dealTo(HandController hand) {
        hand.here(getDeck().nextCard());
    }

    public void displayCards(HandController hand) {
        cardDisplayer.displayCards(hand);
    }

    public DeckOfCards getDeck() {
        return deck;
    }

    public boolean isDealerWin() {
        return gameStatus.isDealerWin();
    }

    public boolean isPlayerWin() {
        return gameStatus.isPlayerWin();
    }

    public boolean isPush() {
        return gameStatus.isPush();
    }

    public void dealerStay() {
        if (dealer.count() > 21) {
            dealerBust();
        } else if (player.count() > dealer.count()) {
            playerWin();
        } else if (dealer.count() > player.count()) {
            dealerWin();
        } else {
            push();
        }

    }

    public void checkHandValues() {
        if (player.canPlay() && dealer.canPlay()) {
            return;
        }
        if (player.size() == 2 && dealer.size() == 2) {
            checkForBlackjack();
        }
        if (gameStatus.isPlayerWin() || gameStatus.isDealerWin()) {
            return;
        }
        if (player.isBust()) {
            playerBust();
            return;
        }
        if (dealer.isBust()) {
            dealerBust();
        }
    }

    public void playerStay() {
        playOutDealer();
    }
}
